use std::future::Future;
use std::ops::{Deref, DerefMut};

use axum::{Router, routing::MethodRouter};
use mf_state::debug;
use tokio::signal;

async fn shutdown_signal() {
    let ctrl_c = async {
        signal::ctrl_c().await.expect("failed to install Ctrl+C handler");
    };

    #[cfg(unix)]
    let terminate = async {
        signal::unix::signal(signal::unix::SignalKind::terminate()).expect("failed to install signal handler").recv().await;
    };

    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => {
            debug!("Ctrl+C pressed");
        },
        _ = terminate => {},
    }
}

pub struct App {
    pub app: Router,
    addr: String,
}

impl App {
    pub fn new(
        app: Router,
        addr: String,
    ) -> Self {
        Self { app, addr }
    }
    pub async fn run_with_graceful_shutdown<F>(
        self,
        signal: F,
    ) where
        F: Future<Output = ()> + Send + 'static,
    {
        let listener = tokio::net::TcpListener::bind(self.addr).await.unwrap();
        debug!("listening on {}", listener.local_addr().unwrap());
        axum::serve(listener, self.app).with_graceful_shutdown(signal).await.unwrap();
    }
    pub async fn run(self) {
        self.run_with_graceful_shutdown(shutdown_signal()).await;
    }
}

impl Deref for App {
    type Target = Router;

    fn deref(&self) -> &Self::Target {
        &self.app
    }
}

impl DerefMut for App {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.app
    }
}

pub struct AppBuilder {
    pub root: Router,
    addr: String,
}

impl Default for AppBuilder {
    fn default() -> Self {
        Self::new()
    }
}

impl AppBuilder {
    pub fn new() -> Self {
        Self { root: Router::new(), addr: "127.0.0.1:3000".to_string() }
    }
    pub fn build(self) -> App {
        App::new(self.root, self.addr)
    }
    pub fn next(
        mut self,
        path: &str,
        router: Router,
    ) -> Self {
        self.root = self.root.nest(path, router);
        self
    }
    pub fn next_map(
        mut self,
        router_map: dashmap::DashMap<String, Router>,
    ) -> Self {
        for (path, router) in router_map {
            self.root = self.root.nest(&path, router);
        }
        self
    }

    pub fn route(
        mut self,
        path: &str,
        method: MethodRouter,
    ) -> Self {
        self.root = self.root.route(path, method);
        self
    }
    pub fn addr(
        mut self,
        addr: String,
    ) -> Self {
        self.addr = addr;
        self
    }
}
